//
// Created by cube on 2020/10/23.
//

#ifndef COMPILERDOMEA1_AOP_IOC_H
#define COMPILERDOMEA1_AOP_IOC_H

#include <functional>
#include <tuple>
#include "../Container/ThreadContainer.h"

#define IOC_BEAN_CLASS(T,...) static IOC::AutowardItem<__VA_ARGS__>& T()\
    {static IOC::AutowardItem<__VA_ARGS__> autoward;return autoward;};

#define IOC_BEAN_OBJECT(T,...) IOC::AutowardItem<__VA_ARGS__> T;

namespace IOC{

    namespace {
        template <typename R,typename ...Args>
        R defualt_Autoward(Args ...args){return R();}

        template <typename R,typename ...Args>
        using Autoward = R(*)(Args...);

        template <typename R,typename ...Args>
        Autoward<R,Args...>& data(){
            static Autoward<R,Args...> autoward = &defualt_Autoward<R,Args...>;
            return autoward;
        }

        template<typename Fun, typename Tuple, std::size_t ...Index,typename ...Args>
        decltype(auto) __invoke_core(Fun &&fun, Tuple &args, std::index_sequence<Index...>,Args& ...params) {
            return fun(std::get<Index>(args)...,params...);
        }
    }

    template<typename Fun, typename Tuple,typename ...Args>
    decltype(auto) invoke(Fun &&fun, Tuple &args,Args& ...params) {
        const static auto paramindex = std::make_index_sequence<std::tuple_size<Tuple>::value>();
        return __invoke_core(fun, args, paramindex,params...);
    }

    template <typename T,typename ...Args>
    T autoward(Args ...args){return data<T,Args...>()(std::forward<Args>(args)...);}

    template <typename T,typename ...Args>
    void registerAutoward(T(*fun)(Args...)){data<T,Args...>() = fun;}

//    template <typename R,typename ...Args>
//    struct AutowardItem {
//        AutowardItem():__data(defualt_Autoward<R,Args...>){}
//        R operator()(Args ...args){ return __data(args...);}
//        void set(Autoward<R,Args...> fun){__data = fun;}
//        Autoward<R,Args...> get(){return __data;}
//    private:
//        Autoward<R,Args...> __data;
//    };
//
//    template <typename F,typename ...Args>
//    struct FunctionPtr {
//    public:
//        FunctionPtr(){}
//        FunctionPtr(F fun,Args& ...args)
//                :__data(fun),__params(std::make_tuple(std::forward<Args>(args)...)){}
//        template <typename ..._Args>
//        auto operator()(_Args ...args){
//            const static auto paramindex = std::make_index_sequence<std::tuple_size<std::tuple<Args...>>::value>();
//            return __invoke_core(__data, __params, paramindex,args...);
//        }
//        void set(F fun){__data = fun;}
//        F get(){return __data;}
//        void setParams(Args& ...args){
//            __params = std::make_tuple(std::forward<Args>(args)...);
//        }
//        std::tuple<Args...> params(){return __params;}
//    private:
//        F __data;
//        std::tuple<Args...> __params;
//    };
//
//    template <typename F,typename ...Args>
//    FunctionPtr<F,Args...> make_FunctionPtr(F fun,Args ...args){
//        return FunctionPtr<F,Args...>(fun,args...);
//    }

};

//AOP----------------------------------

namespace AOP{
    template <typename T>
    class Aspect{
    public:
        Aspect(T& data):__data(data){}
        virtual ~Aspect(){}
        T* operator->(){return &__data;}
        T operator*(){return __data;}
    protected:
        T&  __data;
    };

    template <typename T,typename AspectFun>
    struct AspectItem{
    protected:
        class AspectCore : public Aspect<T>{
        public:
            AspectCore(T& data,AspectFun& start,AspectFun& end)
            :Aspect<T>(data),__end(end){ start(data);}
            ~AspectCore(){__end(this->__data);}
        private:
            AspectFun& __end;
        };
    public:
        AspectItem(T data,AspectFun start,AspectFun end)
        :__data(data),__start(start),__end(end){}
        AspectCore operator()(){return AspectCore(__data,__start,__end);}
        AspectFun start(){return __start;}
        void setStart(const AspectFun& start){__start = start;}
        AspectFun end(){return __end;}
        void setEnd(const AspectFun& end){__end = end;}
        void set(const AspectFun& start,const AspectFun& end)
        {__start = start;__end = end;}
        const T& data(){return __data;}
        void setData(const T& data){__data = data;}
    private:
        T __data;
        AspectFun __start;
        AspectFun __end;
    };

    template <typename T, typename AspectFun>
    struct Aspect_Superposition{
    protected:
        class AspectCore : public Aspect<T>{
        public:
            AspectCore(T& data,std::set<AspectFun>& starts,std::set<AspectFun>& ends)
            :Aspect<T>(data),__ends(ends){for (auto f : starts)f(data);}
            ~AspectCore(){for (auto f : this->__ends)f(this->__data);}
        private:
            std::set<AspectFun>& __ends;
        };
    public:
        Aspect_Superposition(T data):__data(data){}
        Aspect_Superposition(T data,AspectFun start,AspectFun end):__data(data){add(start,end);}
        AspectCore operator()(){return AspectCore(__data,__starts,__ends);}
        std::set<AspectFun>& starts(){return __starts;}
        std::set<AspectFun>& ends(){return __ends;}
        void add(const AspectFun& start,const AspectFun& end){__starts.insert(start);__ends.insert(end);}
        const T& data(){return __data;}
        void setData(const T& data){__data = data;}
    private:
        T __data;
        std::set<AspectFun> __starts;
        std::set<AspectFun> __ends;
    };

};

#endif //COMPILERDOMEA1_AOP_IOC_H
